package entidades;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import persistencia.Persistencia;
import util.*;

/**
 * Classe que gerencia eventos e salas
 * 
 * @author Raildo, Gabriel, Tales e Arthur
 * @version 1.0 Classe que gerencia os objetos da classe Sala e da classe
 *          Eventos.
 * 
 */
public class Gerenciador  implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -906572051099606037L;
	List<Sala> listaDeSalas;
	List<Evento> listaDeEventos;

	/**
	 * Contrutor do gerenciador
	 * 
	 */
	public Gerenciador() {
		listaDeSalas = new ArrayList<Sala>();
		listaDeEventos = new ArrayList<Evento>();
	}

	public List<Evento> getListaDeEventos() {
		return this.listaDeEventos;
	}

	public List<Sala> getListaDeSalas() {
		return this.listaDeSalas;
	}

	/**
	 * Metodo para criar uma sala a partir dos atributos passados e adiciona-la
	 * a List<Salas>.
	 * 
	 * @param id
	 *            id da sala
	 * @param capacidade
	 *            capacidade da sala
	 * @param finalidade
	 *            finalidade da sala
	 * @param tipo
	 *            tipo da sala
	 * @throws Exception
	 *             Se ja exista uma sala com o id que foi passado
	 */
	public void adicionarSala(String id, int capacidade, String finalidade,
			String tipo) throws Exception {

		if (!verificaDisponibilidadeDeSalaPeloId(id)) {
			throw new Exception("Ja existe sala com esta identificacao.");
		}

		guardaSala(new Sala(id, capacidade, finalidade, tipo));
	}

	/**
	 * Metodo para criar uma sala a partir dos atributos passados e adiciona-la
	 * a List<Salas>.
	 * 
	 * @param id
	 *            id da sala
	 * @param capacidade
	 *            capacidade da sala
	 * @param finalidade
	 *            finalidade da sala
	 * @param tipo
	 *            tipo da sala
	 * @param apelido
	 *            apelido da sala
	 * @throws Exception
	 *             Se ja exista uma sala com o id que foi passado
	 */
	public void adicionarSala(String id, int capacidade, String finalidade,
			String tipo, String apelido) throws Exception {

		if (!verificaDisponibilidadeDeSalaPeloId(id)) {
			throw new Exception("Ja existe sala com esta identificacao.");
		}

		guardaSala(new Sala(id, capacidade, finalidade, tipo, apelido));
	}

	/**
	 * Metodo para criar uma sala a partir dos atributos passados e adiciona-la
	 * a List<Salas>.
	 * 
	 * @param id
	 *            id da sala
	 * @param capacidade
	 *            capacidade da sala
	 * @param finalidade
	 *            finalidade da sala
	 * @param tipo
	 *            tipo da sala
	 * @param apelido
	 *            apelido da sala
	 * @param aberto
	 *            estado da sala
	 * @throws Exception
	 *             Se ja exista uma sala com o id que foi passado
	 */
	public void adicionarSala(String id, int capacidade, String finalidade,
			String tipo, String apelido, boolean aberto) throws Exception {
		if (!verificaDisponibilidadeDeSalaPeloId(id)) {
			throw new Exception("Ja existe sala com esta identificacao.");
		}
		guardaSala(new Sala(id, capacidade, finalidade, tipo, apelido, aberto));
	}

	/**
	 * Método para alocar um Evento já criado a uma Sala.
	 * 
	 * @param idEvento
	 *            - identificação do evento a ser alocado.
	 * @param idSala
	 *            - identificação da sala onde o evento será alocado.
	 * @throws Exception
	 *             - Lança exceção se a Sala ou o Evento não existirem, ou se o
	 *             Evento já estiver alocado, ou se a Sala informada for um
	 *             Escritório, ou se a Sala informada for um Laboratório aberto
	 *             ou se um evento com repetições for passado para uma Sala de
	 *             Conferência Normal.
	 */
	public void alocarEvento(String idEvento, String idSala) throws Exception {

		Sala sala = localizaSalaPorId(idSala);
		Evento evento = localizaEventoPorId(idEvento);

		if (sala == null || evento == null) {
			throw new Exception("Sala/Evento nao existe.");
		}

		if (evento.estaAlocado()) {
			throw new Exception("O Evento ja foi alocado anteriormente.");
		}

		if (sala.getFinalidade().equalsIgnoreCase(
				FinalidadeTipoSala.ESCRITORIO.getFinalidade())) {
			throw new Exception("Escritorios nao sao escalonaveis.");
		}

		if (sala.getFinalidade().equalsIgnoreCase("Laboratorio")
				&& (sala.isAberto())) {
			throw new Exception("Laboratorios abertos nao sao escalonaveis.");
		}

		if (sala.getFinalidade().equalsIgnoreCase(
				FinalidadeTipoSala.SALA_DE_CONFERENCIA_NORMAL.getFinalidade())
				&& sala.getTipo()
						.equalsIgnoreCase(
								FinalidadeTipoSala.SALA_DE_CONFERENCIA_NORMAL
										.getTipo())
				&& (evento.getRepeticoes() > 0)) {
			throw new Exception(
					"Salas de Conferencia do tipo Normal nao sao escalonaveis para eventos repetitivos.");
		}

		// verificaDisponibilidadeSalaHora(sala, evento);

		sala.alocarEvento(evento);
		evento.setEstaAlocado(true);
	}

	/**
	 * Método para recuperar um atributo da classe Sala
	 * 
	 * @param id
	 *            - identificação da sala a ser procurada.
	 * @param atributo
	 *            - atributo da sala a ser buscada
	 * @return - retorna o valor do atributo a ser pesquisado
	 * @throws Exception
	 *             - Lança Exceção se o objeto da classe Sala for null, ou se
	 *             for pedido o atributo "tipo" do Escritorio.
	 */
	public String getAtributoSala(String id, String atributo) throws Exception {
		Sala sala = localizaSalaPorId(id);

		if (sala == null) {
			throw new Exception("Sala nao existe.");
		}

		if (sala.getFinalidade().equalsIgnoreCase("escritorio")
				&& atributo.equalsIgnoreCase("tipo")) {
			throw new Exception("Atributo invalido");
		}

		if (atributo.equalsIgnoreCase("finalidade")) {
			return sala.getFinalidade();
		}

		if (atributo.equalsIgnoreCase("capacidade")) {
			return String.valueOf(sala.getCapacidade());
		}

		if (atributo.equalsIgnoreCase("apelido")) {
			return sala.getApelido();
		}

		if (atributo.equalsIgnoreCase("tipo")) {
			return sala.getTipo();
		}

		return "Atributo Invalido";
	}

	/**
	 * Metodo para remover uma Sala do sistema. Todos os eventos da sala serão
	 * setados como não-alocados
	 * 
	 * @param id
	 *            - id da sala a ser ecluida
	 * @throws Exception
	 *             - Uma Exceção será lançada se a identificação da sala for
	 *             null.
	 */
	public void removerSala(String id) throws Exception {
		Sala sala = localizaSalaPorId(id);

		if (sala == null) {
			throw new Exception("Sala nao existe.");
		}

		for (int i = 0; i < sala.getEventos().size(); i++) {
			sala.getEventos().get(i).setEstaAlocado(false);
		}

		listaDeSalas.remove(sala);

	}

	public void setListaDeSalas(List<Sala> listaDeSalas) {
		this.listaDeSalas = listaDeSalas;
	}

	public void setListaDeEventos(List<Evento> listaDeEventos) {
		this.listaDeEventos = listaDeEventos;
	}

	/**
	 * Método para criar um Evento baseado nos atributos fornecidos.
	 * 
	 * @param id
	 *            - identificação do Evento
	 * @param nome
	 *            - nome do Evento
	 * @param inicio
	 *            - data e hora de inicio
	 * @param fim
	 *            - data e hora do fim do evento
	 * @param area
	 *            - area de afinidade do evento
	 * @param contato
	 *            - nome do responsavel pelo Evento
	 * @throws Exception
	 *             - Uma Exceção será lançada caso a identificação fornecida já
	 *             pertencer a outro Evento já criado.
	 */
	public void adicionarEvento(String id, String nome, String inicio,
			String fim, String area, String contato) throws Exception {

		if (!verificaDisponibilidadeDeEventoPeloId(id)) {
			throw new Exception("Ja existe evento com esta identificacao.");
		}

		guardaEvento(new Evento(id, nome, inicio, fim, area, contato));
	}

	/**
	 * Método para criar um Evento baseado nos atributos fornecidos.
	 * 
	 * @param id
	 *            - identificação do Evento
	 * @param nome
	 *            - nome do Evento
	 * @param inicio
	 *            - data e hora de inicio
	 * @param fim
	 *            - data e hora do fim do evento
	 * @param area
	 *            - area de afinidade do evento
	 * @param contato
	 *            - nome do responsavel pelo Evento
	 * @param repeticoes
	 *            - numero de semanas em que o evento irá repetir
	 * @throws Exception
	 *             - Uma Exceção será lançada caso a identificação fornecida já
	 *             pertencer a outro Evento já criado.
	 */
	public void adicionarEvento(String id, String nome, String inicio,
			String fim, String area, String contato, int repeticoes)
			throws Exception {

		if (!verificaDisponibilidadeDeEventoPeloId(id)) {
			throw new Exception("Ja existe evento com esta identificacao.");
		}
		guardaEvento(new Evento(id, nome, inicio, fim, area, contato,
				repeticoes));
	}

	/**
	 * Metodo para cancelar um Evento previamente criado
	 * 
	 * @param id
	 *            - identificação do evento criado.
	 * @throws Exception
	 *             - Uma Exceção será lançada se a identificação não pertencer a
	 *             nenhum Evento.
	 */
	public void cancelarEvento(String id) throws Exception {

		Evento eventoASerCancelado = localizaEventoPorId(id);

		if (eventoASerCancelado == null) {
			throw new Exception("Evento nao existe.");
		}

		if (eventoASerCancelado.estaAlocado()) {
			for (int i = 0; i < listaDeSalas.size(); i++) {
				Sala sala = listaDeSalas.get(i);
				for (int j = 0; j < sala.getEventos().size(); j++) {
					Evento evento = sala.getEventos().get(j);
					if (eventoASerCancelado.equals(evento)) {
						sala.desalocarEvento(evento);
						listaDeEventos.remove(evento);
					}
				}
			}
		} else {
			for (int i = 0; i < listaDeEventos.size(); i++) {
				if (listaDeEventos.get(i).equals(eventoASerCancelado)) {
					listaDeEventos.remove(eventoASerCancelado);
				}
			}
		}

	}

	/**
	 * 
	 * @param id
	 *            - identificação do Evento a ser desalocado. Entende-se por
	 *            desalocar, o processo de desvincular um evento a sua Sala.
	 * @throws Exception
	 *             - Uma Esceção será lançada se o Evento já estiver alocado.
	 */
	public void desalocarEvento(String id) throws Exception {

		if (!eventoAlocado(id)) {
			throw new Exception("O evento nao esta alocado.");
		}

		for (int i = 0; i < listaDeSalas.size(); i++) {
			for (int j = 0; j < listaDeSalas.get(i).getEventos().size(); j++) {
				if (listaDeSalas.get(i).getEventos().get(j).getId()
						.equalsIgnoreCase(id)) {
					listaDeSalas.get(i).desalocarEvento(
							listaDeSalas.get(i).getEventos().get(j));
				}
			}
		}
	}

	private boolean eventoAlocado(String id) {

		for (int i = 0; i < listaDeEventos.size(); i++) {
			Evento ev = listaDeEventos.get(i);
			if (ev.getId().equalsIgnoreCase(id) && ev.estaAlocado()) {
				return true;
			}
		}
		return false;

	}

	/**
	 * Metodo para recuperar uma String indicando um par Sala-Evento que contenha o atributo especificado e
	 * o valor fornecido.
	 * 
	 * @param atributo
	 * @param valor
	 * @return
	 * @throws Exception
	 */
	public String localizarEvento(String atributo, String valor)
			throws Exception {

		if (valor == null || valor.equals("") || atributo == null
				|| atributo.equals("")) {
			throw new Exception("Entrada Invalida");
		}

		List<String> eventosLocalizados = new ArrayList<String>();

		for (int i = 0; i < listaDeSalas.size(); i++) {
			for (int j = 0; j < listaDeSalas.get(i).getEventos().size(); j++) {
				if (atributo.equalsIgnoreCase("nome")) {
					if (listaDeSalas.get(i).getEventos().get(j).getNome()
							.equalsIgnoreCase(valor)
							|| StringsUtil.contemPalavra(valor, listaDeSalas.get(i).getEventos()
									.get(j).getNome())) {

						eventosLocalizados.add(listaDeSalas.get(i).getId() + ":"
								+ listaDeSalas.get(i).getEventos().get(j).getId());

					}
				}

				else if (atributo.equalsIgnoreCase("contato")) {
					if (listaDeSalas.get(i).getEventos().get(j).getContato()
							.equalsIgnoreCase(valor)
							|| StringsUtil.contemPalavra(valor, listaDeSalas.get(i).getEventos()
									.get(j).getContato())) {

						eventosLocalizados.add(listaDeSalas.get(i).getId() + ":"
								+ listaDeSalas.get(i).getEventos().get(j).getId());

					}
				}

				else if (atributo.equalsIgnoreCase("horario")) {
					if (listaDeSalas.get(i).getEventos().get(j).getIntervaloTempo()
							.conflitoHorario(valor)) {

						eventosLocalizados.add(listaDeSalas.get(i).getId() + ":"
								+ listaDeSalas.get(i).getEventos().get(j).getId());
					}
				}
			}
		}

		if (eventosLocalizados.size() == 0) {
			return "Nenhum evento encontrado.";
		} else {

			return StringsUtil.formataStringSalaEvento(eventosLocalizados);
		}

	}

	/**
	 * Metodo para recuperar um atributo especificado por parametro de um Evento.
	 * @param id - identificação do Evento
	 * @param atributo - atributo do evento a ser pesquisado
	 * @return - o valor do atributo do Evento
	 * @throws Exception - Uma exceção será lançada se não for localizado um Evento pelo atributo passado.
	 */
	public String getAtributoEvento(String id, String atributo)
			throws Exception {
		Evento evento = localizaEventoPorId(id);

		if (evento == null) {
			throw new Exception("Evento nao existe.");
		}

		if (atributo.equalsIgnoreCase("nome")) {
			return evento.getNome();
		} else if (atributo.equalsIgnoreCase("inicio")) {
			return evento.getInicio();
		} else if (atributo.equalsIgnoreCase("fim")) {
			return evento.getFim();
		} else if (atributo.equalsIgnoreCase("area")) {
			return String.valueOf(evento.getArea());
		} else if (atributo.equalsIgnoreCase("repeticoes")) {
			return String.valueOf(evento.getRepeticoes());
		}

		throw new Exception("Atributo nao existe.");

	}
	/**
	 * localiza a sala atraves de seu id 
	 * @param id identificacao da Sala
	 * @return a sala pesquisada, null caso ela nao exista
	 */
	private Sala localizaSalaPorId(String id) {

		if (listaDeSalas.size() > 0) {
			for (int indice = 0; indice < listaDeSalas.size(); indice++) {

				if (listaDeSalas.get(indice).getId().equals(id)) {

					return listaDeSalas.get(indice);
				}
			}
		}

		return null;
	}

	/**
	 * Metodo para localizar um Evento pela identificação fornecida.
	 * @param id - identificação do Evento
	 * @return - o Evento localizado
	 */	
	public Evento localizaEventoPorId(String id) {

		if (listaDeEventos.size() > 0) {
			for (int indice = 0; indice < listaDeEventos.size(); indice++) {

				if (listaDeEventos.get(indice).getId().equals(id)) {

					return listaDeEventos.get(indice);
				}
			}
		}
		return null;
	}

	private void guardaSala(Sala sala) {
		listaDeSalas.add(sala);
	}

	private void guardaEvento(Evento evento) {
		listaDeEventos.add(evento);
	}
	/**
	 * Metodo que verifica a disponibilidade da sala atraves do id
	 * @param id identificacao da sala
	 * @return um boolean indicando se a sala foi localizada
	 * @throws Exception
	 */
	private boolean verificaDisponibilidadeDeSalaPeloId(String id)
			throws Exception {
		if (localizaSalaPorId(id) == null) {
			return true;
		}
		return false;
	}
	/**
	 * Metodo que verifica a disponibilidade do evento atraves do id
	 * @param id identificacao do evento
	 * @return um boolean indicando se o evento foi localizado
	 * @throws Exception
	 */
	private boolean verificaDisponibilidadeDeEventoPeloId(String id)
			throws Exception {
		if (localizaEventoPorId(id) == null) {
			return true;
		}
		return false;
	}

	/**
	 * Metodo para encerrar o sistema
	 */
	public void encerrarSistema() {
		System.exit(0);
	}

	/**
	 * Metodo para limpar a base de dados da memoria e da persistencia 
	 */
	public void zerarSistema() {
		listaDeSalas.clear();
		listaDeEventos.clear();
		zerarPersistencia();

	}
	
	private void zerarPersistencia(){
		Persistencia.zerarSistema();
	}

}